Skip to content

本篇学习日志参考了 掘金文章(JavaScript新特性最全指南:ES2024 ~ ES2016 ) 与 MDN。 通过掘金文章提供罗列的新特性, 逐个在MDN查找后, 转换成本人熟悉的文章结构和记录风格。 因为本人学艺不精,在参数类型定义,及其他地方有优化空间。

ES2024

【类库】

isWellFormed() 单独代理项格式监测

String.prototype.isWellFormed()

字符串是否包含单独代理项 传递的字符串格式不正确, encodeURI 会抛出错误,可用该方法做预判

参数:/ 返回值:boolean

js
const illFormed = "https://example.com/search?q=\uD800";

try {
  encodeURI(illFormed);
} catch (e) {
  console.log(e); // URIError: URI malformed
}

if (illFormed.isWellFormed()) {
  console.log(encodeURI(illFormed));
} else {
  console.warn("Ill-formed strings encountered."); // Ill-formed strings encountered.
}

resizable 二进制对象是否可调大小

ArrayBuffer.prototype.resizable

当创建实例时,设置了maxByteLength最大字节,则可调

返回值:boolean

js
const buffer = new ArrayBuffer(8, { maxByteLength: 16 });

if (buffer.resizable) {
  console.log("缓冲区可以调整大小!");
  buffer.resize(12);
}

ES2023

【类库】

findLast() 反向寻找

Array.prototype.findLast()

反向在数组中寻找匹配元素,类似arr.find

参数:callback 回调参数:元素, 索引, 数组本身 返回值:元素 | undefined 注:只匹配第一个符合的元素

js
const inventory = [
  { name: "apples", quantity: 2 },
  { name: "bananas", quantity: 0 },
  { name: "fish", quantity: 1 },
  { name: "cherries", quantity: 5 },
];

// 库存低时返回 true
function isNotEnough(item) {
  return item.quantity < 2;
}

console.log(inventory.findLast(isNotEnough));
// { name: "fish", quantity: 1 }

findLastIndex() 反向寻找索引

Array.prototype.findLastIndex()

反向在数组中寻找匹配元素的索引,类似arr.findIndex

参数:callback 回调参数:遍历元素, 索引, 数组本身 返回值:索引 | -1 注:只匹配第一个符合的元素

js
function isPrime(element) {
  if (element % 2 === 0 || element < 2) {
    return false;
  }
  for (let factor = 3; factor <= Math.sqrt(element); factor += 2) {
    if (element % factor === 0) {
      return false;
    }
  }
  return true;
}

console.log([4, 6, 8, 12].findLastIndex(isPrime)); // -1,没有找到
console.log([4, 5, 7, 8, 9, 11, 12].findLastIndex(isPrime)); // 5

toReversed()数组翻转

Array.prototype.toReversed()

原本的reversed()方法,会改变原数组,该方法不改变

参数: / 返回值: 新数组 注:原数组不变

js
const items = [1, 2, 3];
console.log(items); // [1, 2, 3]

const reversedItems = items.toReversed();
console.log(reversedItems); // [3, 2, 1]
console.log(items); // [1, 2, 3]

toSorted()数组排序

Array.prototype.toSorted()

原本的sort()方法,会改变原数组,该方法不改变

参数: undefind | callback 回调参数:上一个元素,下一个元素 返回值: 新数组 注:原数组不变

js
// 不传入函数
toSorted()

// 传入箭头函数
toSorted((a, b) => { /* … */ })

// 传入比较函数
toSorted(compareFn)

// 內联比较函数
toSorted(function compareFn(a, b) { /* … */ })

const months = ["Mar", "Jan", "Feb", "Dec"];
const sortedMonths = months.toSorted();
console.log(sortedMonths); // ['Dec', 'Feb', 'Jan', 'Mar']
console.log(months); // ['Mar', 'Jan', 'Feb', 'Dec']

const values = [1, 10, 21, 2];
const sortedValues = values.toSorted((a, b) => a - b);
console.log(sortedValues); // [1, 2, 10, 21]
console.log(values); // [1, 10, 21, 2]

toSpliced()数组替换

Array.prototype.toSpliced()

原本的splice()方法,会改变原数组,该方法不改变

重载1:删除后续 参数: number开始索引

重载2:删除节选 参数: number开始索引, number删除长度,

重载3:替换节选 参数: number开始索引, number删除长度, element1插入内容1, element2插入内容2 elementN 返回值: 新数组 注:原数组不变

js
splice(start)
splice(start, deleteCount)
splice(start, deleteCount, item1)
splice(start, deleteCount, item1, item2, itemN)

const months = ["Jan", "Mar", "Apr", "May"];

// 在索引 1 处添加一个元素
const months2 = months.toSpliced(1, 0, "Feb");
console.log(months2); // ["Jan", "Feb", "Mar", "Apr", "May"]

// 从第 2 个索引开始删除两个元素
const months3 = months2.toSpliced(2, 2);
console.log(months3); // ["Jan", "Feb", "May"]

// 在索引 1 处用两个新元素替换一个元素
const months4 = months3.toSpliced(1, 1, "Feb", "Mar");
console.log(months4); // ["Jan", "Feb", "Mar", "May"]

// 原数组不会被修改
console.log(months); // ["Jan", "Mar", "Apr", "May"]

with()数组修改

原本通过直接等号赋值数组索引,改变数组内容。 该方法是不改变数组版本

参数: number索引 any属性值 返回值:新数组 注: 索引为负数表示倒序查询 超过查询极限则报错

js
// 原本改变数组内容
const arr = [1, 2, 3];
arr[0] = 4;  // [4, 2, 3]

const arr = [1, 2, 3, 4, 5];
console.log(arr.with(2, 6)); // [1, 2, 6, 4, 5]
console.log(arr); // [1, 2, 3, 4, 5]

【语法】

node.js 执行js文件语法糖

#!/usr/bin/env node

命令行工具执行js文件,需要加上node前缀 在执行的js中首行添加该命令,可省去node前缀

js
// 添加后的js执行文件
#!/usr/bin/env node
console.log('hello')


// 例1
node ./test.js  // 正常执行,未报错

// 例2
./index.js  // 未添加时执行,报错
./index.js: line 1: syntax error near unexpected token "javascript"
./index.js: 1ine 1: `console.log("Javascript")`

// 例3
./index.js  // 添加后执行,未报错

允许Symbol作为WeakMap的键

原本WeakMap键值对的键只允许使用对象类型,现在允许使用Symbol类型

js
const weak = new WeakMap();
const key = Symbol('hello');
weak.set(key, 'world');

ES2022

【类库】

at() 数组索引查询

Array.prototype.at()

同括号表示法,但可以使用负数。 适合在未知数组长度时,寻找最后一项。

参数:number索引 返回值:any数组元素

js
const arr = [1,2,3];

// old
arr[1];  // 2
arr[arr.length - 1] // 3

// now
arr.at(1); // 2
arr.at(-1); // 3

hasOwn() 对象检查属性

Array.prototype.at()

Object的静态方法,用于确认对象有没有某个属性。 原本的hasOwnProperty方法,因为任何类型都能从原型链上找到,所以有了新版。 个人感受,纯属脱裤子放屁,需要确认的场景大多可以通过?.链式操作规避。

参数: object被检查对象 string属性名 返回值:boolean

js
const obj = {a: 1};

// old,通过原型链确认
obj.hasOwnProperty('a'); // true

// now
Object.hasOwn(obj,'a'); // true

【语法】

class 属性定义简化

原本class定义属性需在constructor内定义,现在可以直接在外部定义。

js
// old
class X{
	constructor(){
		this.a = 'a';
	}
}

// now
class X{
	a = 'a';
}

class 成员私有化

首字母#的属性无法被外部读取 在ES2022之前,并没有实际意义上的私有字段。大家形成一种默契,通常用下划线_开头的字段名来表示私有字段,但是这些字段还是可以手动更改的。 ES2022给我们提供了更加安全便捷的私有字段定义方法,就是以#开头命名的字段,都会被当成私有字段,在class外部是没办法直接读取、修改这些私有字段的。

js
class My {
    #a = 'a';
    b = 'b'
    c;
    constructor() {
        this.c = this.#a + 'c';
    };
};

const bob = new My();

console.log(bob.#a);  // 报错
console.log(bob.b);  // 'b'
console.log(bob.c);  // 'ac'

class 静态初始化块

用于在初始化时处理静态内容的一个代码块

js
class My {
    a = 'a';

	// now
    static {
        this.a = 'b'
    }
}

const baobao = new My;
console.log(baobao.a);  // 'a'
console.log(My.a);  // 'b'

await 可单独在外部

原本await必须在async函数内,现在可在 ES Modules模块化文件最外层直接使用。

js
try {
    await fetch('url')
} catch { 
	// ...
}

regexp/d 正则匹配范围修饰

新的修饰符,原本正则匹配只会返回开始匹配的索引index, 使用该修饰符时,多了获取匹配到内容的区间的属性indices。

js
const text = 'abbbcccddebc';
const reg = /bc/
const reg_has_d = /bc/d

// old
console.log(reg.exec(text))
// [ 'bc', index: 3, input: 'abbbcccddebc', groups: undefined ]

// now
console.log(reg_has_d.exec(text))
// [ 'bc', index: 3, input: 'abbbcccddebc', groups: undefined, indices: [ [ 3, 5 ], groups: undefined ] ]

Error_cause错误传递

构造函数Error的第二个参数options,新增cause字段,用于将信息传递给下一个捕获的地方。

js
try {
	await fetch('url')
        .catch(err => {
            throw new Error('no', { cause: 'is_err' })
        });
} catch(e){
	// old
	console.log(e);  //  一大串具体错误,适合调试,但不适合显示
	
	// now
	console.log(e.cause);  // is_err
}

ES2021

【类库】

replaceAll() 匹配替换全部

String.prototype.replaceAll()

等同于在replace时,第一个参数为正则时/g匹配

参数: string | regexp 匹配的内容 string | callback 替换内容 回调参数:与其他正则相关回调相同,不展开讲 返回值: 新字符串 注:如果使用regexp必须加/g全局匹配,否则报错

js
// old
'aabc'.replace(/a/, '哈哈')  // 哈哈abc
'aabc'.replace('a', '哈哈')  // 哈哈abc
'aabc'.replace(/a/g, '哈哈')  // 哈哈哈哈bc

// now
'aabc'.replaceAll('a', '哈哈')  // 哈哈哈哈bc

any() 异步全部匹配

Promise.any()

Promise的静态方法,多个promise中获取最快的成功结果。 与Promise.race()不同的是,race求一个最快的结果不论好坏,any求一个最快的成功结果。

参数: promise实例1 promise实例2 promise实例N 返回值:整合后的promise实例,可直接用then链式调用。 注:catch部分在所有异步都失败时给出。

js
const promise1 = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'quick')
    setTimeout(reject, 90, 'quick')
});

const promise2 = new Promise((resolve) => setTimeout(resolve, 500, 'slow'));

Promise.any([promise1, promise2])
    .then((value) => console.log('any:' + value))  // any: slow
    .catch(() => console.log('err'));  // 只有在两者都失败时才显示

Promise.race([promise1, promise2])
    .then((value) => console.log('race:' + value))
    .catch(() => console.log('race:' + 'err'));  // race:err

WeakRef 弱引用对象

疑似跟垃圾回收机制有关,weakRef对象被回收时不会被阻止

js
const obj = {a: 1};
const ref = new WeakRef(obj);
console.log(ref)  // WeakRef {}

// 使用原型链方法deref获取对象
console.log(ref.deref())  // { a: 1 }

// 暂时想不到回收机制相关的应用场景,用例待验证

【语法】

number_00 数字分隔符

意义不变,但使人类舒适

1_0 === 10; // true
7_00 - 1_00 // 600

逻辑赋值运算符

若能短路到右边,则将右边赋值给左边 短路运算的复杂版,难搞,且使用场景不多(面试题!!!),不推荐用

js
// 或
var a = false;
var b = 2;
a ||= b; 
// 左边为false,短路,执行右边
// 将右边的值赋值给左边
console.log(a); // 2

// 且
var a = 2;
var b = 0;
a &&= b; 
// 左边为true,短路,执行右边
// 将右边赋值给左边
console.log(a); // 0

// ??
var a = null;
var b = 2;
// 左边为null或undef,执行右边
// 将右边赋值给左边
console.log(a); // 2

ES2020

【类库】

matchAll() 正则查询全部

String.prototype.matchAll()

match()的增强版,返回值类似exec()的返回值

参数:string | regexp 匹配规则 返回值:Iterator迭代器 注:只想得到匹配的元素,建议使用结构更清晰的match

js
// old
'aabc'.match(/a/g); //  ['a', 'a']

// now
// 通过展开运算符,化为数组结构
[...'aabc'.matchAll(/a/g)]; // [['a', index:0, input:'aabc',...], ['a', index: 1]]

// 通过next展开迭代器
const regexp = 'aabc'.matchAll('a');
console.log(reg.next()); // {done: false, value: ['a',index:0,input:...]};
console.log(reg.next()); // {done: false, value: ['a',index:1,input:...]};
console.log(reg.next()); // {done: true, value: undefined};

allSettled() 异步全部匹配

Promise.allSettled()

all的增强版,当all执行过程中,有报错则会直接中断。 allSetLed则会返回状态和错误。

参数: promise实例1 promise实例2 promise实例N 返回值:{stats状态, value值}[ ]

js
const promise1 = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'good_1')
    setTimeout(reject, 190, 'err_1')
});

const promise2 = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'good_2')
    setTimeout(reject, 400, 'err_2')
});

Promise.all([promise1, promise2]).then((value) => console.log(value));
// [ 'good_1', 'good_2' ]

Promise.allSettled([promise1, promise2]).then((value) => console.log(value));
// [{ status: 'fulfilled', value: 'good_1' },{ status: 'fulfilled', value: 'good_2' }]

BigInt 大整型对象

原本js最大整数为 Math.pow(2, 53) -1,超出则精度丢失。

注:BigInt不可与number运算,但可比较

js
BigInt("0b11111111111111111111111111111111111111111111111111111");
BigInt(2) - BigInt(1); // 1n
1n === BigInt(1) // true
1n < 2 // true
【语法】

import 动态导入

可以把import作为一个变量,动态导入,仅可在ES modules模块化js中使用 很好用,配合node.js的file模块,可以批量导入图片

js
// 被导入文件 test.js
export function haha() {
    console.log('haha')
}

// 使用文件
const urlList = ['./test.js'];
urlList.forEach(url => {
    import(url).then((module) => {
        module.haha();
    })
})
// haha

globalThis全局对象

新的关键字,在node环境和游览器环境指向不同

js
window === globalThis // 游览器环境
global === globalthis // Node.js 环境

?.可选操作符

使用点操作符,链式调用属性,若上一层未查询到,则直接报错。 该操作符可规避此类报错。

js
const obj = null;
obj.a; // 报错
obj?.a; // undefined

空值合并运算符

类似 || 或运算,在此基础上,更语义化和严谨的方案。 只有左侧为 null 或 undefined 时,才返回右侧操作符。

js
// 或运算
0 || 1;  // 1
null || 1:  // 1
false || 1;  // 1
// 因为左侧能隐式转换成false,所以返回右侧

// 空值合并运算
0 ?? 1;  //0
false ?? 1;  // false
null ?? 1;  // 1
// 只有null和undefined可返回右侧,其余均返回左侧

ES2019

【类库】

description 唯一值描述

Symbol.prototype.description

可快速获取symbol值的描述内容

返回值:value描述

js
Symbol("desc").toString(); // "Symbol(desc)"
Symbol("desc").description; // "desc"
Symbol("").description; // ""
Symbol().description; // undefined

// 内置通用(well-known)symbol
Symbol.iterator.toString(); // "Symbol(Symbol.iterator)"
Symbol.iterator.description; // "Symbol.iterator"

// global symbols
Symbol.for("foo").toString(); // "Symbol(foo)"
Symbol.for("foo").description; // "foo"

formEntries() 对象转二维数组

Object.fromEntries()

对象的静态方法,二维数组 转 对象。 entries的逆向转换。

参数:array二维数组 | Map键值对 返回值:object对象 注:Map的键如果是对象,则返回object Object

js
// 正常使用
const entries = [
  ['foo', 'bar'],
  ['baz', 42],
];
const obj = Object.fromEntries(entries);
console.log(obj); // { foo: "bar", baz: 42 }

const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42],
]);
const obj = Object.fromEntries(entries);
console.log(obj); // { foo: "bar", baz: 42 }

// map的键是对象
const entries = new Map([
  [{a:1}, 'bar'],
  ['baz', 42],
]);
const obj = Object.fromEntries(entries);
console.log(obj); // { [object Object]: "bar", baz: 42 }

// 值是对象
const entries = new Map([
  ['foo', {a:1}],
  ['baz', 42],
]);
const obj = Object.fromEntries(entries);
console.log(obj); // { foo: { a: 1 }, baz: 42 }

entries() 二维数组转对象

Object.entries()

对象的静态方法,对象 转 二维数组。 fromEntries的逆向转换。

参数:object对象 返回值:array二维数组

js
const obj = {
  a: 'string',
  b: 1,
};
console.log(Object.entries(obj));
// [["a", "string"], ["b", 1]]

trimStart() 消除前空格

String.prototype.trimStart()

trim方法的细化版本,后面的空格不会处理

参数:/ 返回值:string 新字符串

js
const greeting = '   a   ';

console.log(greeting);  // "   a   "
console.log(greeting.trimStart());  // "a   "

trimEnd() 消除前空格

String.prototype.trimEnd()

trim方法的细化版本,前面的空格不会处理

参数:/ 返回值:string 新字符串

js
const greeting = '   a   ';

console.log(greeting);  // "   a   "
console.log(greeting.trimEnd());  // "   a"

【语法】

catch 参数可选

原本catch必须有参数结构,现在可以省略

js
// old
try{

} catch (err){

};

// now
try{

} catch {

}

Function toString() 函数返回增强

原本返回函数源码时,会删除注释和空格

js
const fn = () => {
    // 注释
    console.log(1);
}
console.log(fn.toString());

// 执行结果
() => {
    // 注释
    console.log(1);
}

JSON.stringify 识别\编码

原本这类编码无法读取,现在扩展了识别的编码范围

注:数据库或不能转换icon编码

js
// old 
let text = JSON.stringify("\u26D4");  // "�"

// now
let text = JSON.stringify("\u26D4");  // 一个小表情

ES2018

【类库】

finally()异步后执行

Promise.prototype.finally()

无论异步成功还是失败,都会执行一遍。避免在then和catch部分写重复代码。

参数:callback回调函数 回调参数:/ 返回值:异步实例

js
const pro = new Promise((resolve) => resolve('a'));

pro
	.then(e => console.log(e))  // a
	.finally(e => console.log(e))  // undefined,没有参数

【语法】

?<>正则分组

正则匹配exec的优化语法,可以直接定义获取元素的组别

js
const regexp = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
const result = regexp.exec('2023-01-01');
console.log(result.groups);  // {year: '2023', month: '01', day: '01'}